package com.jiangxjsy.gateway.service.security.impl;

import com.jiangxjsy.core.constant.BasicConstant;
import com.jiangxjsy.core.constant.RedisCacheConstant;
import com.jiangxjsy.core.constant.TokenConstant;
import com.jiangxjsy.core.exception.BasicException;
import com.jiangxjsy.core.jwt.JwtCore;
import com.jiangxjsy.core.redis.Redis;
import com.jiangxjsy.core.wrap.Result;
import com.jiangxjsy.core.wrap.ResultCode;
import com.jiangxjsy.core.wrap.ResultHelper;
import com.jiangxjsy.gateway.client.AuthorizationClient;
import com.jiangxjsy.core.exception.NoLoginException;
import com.jiangxjsy.gateway.property.GatewayProperty;
import com.jiangxjsy.gateway.property.JwtProperty;
import com.jiangxjsy.gateway.property.TokenProperty;
import com.jiangxjsy.gateway.service.security.PermissionService;
import com.jiangxjsy.model.core.entity.UserExtra;
import com.jiangxjsy.model.core.entity.UserMeta;
import org.springframework.http.HttpCookie;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.List;

/**
 * wukong-crm-master
 * 蒋胜宇
 * 2025/5/30 15:37
 */

@Service
public class PermissionServiceImpl implements PermissionService {
    @Resource
    private AuthorizationClient authorizationClient;

    @Resource
    private GatewayProperty gatewayProperty;

    @Resource
    private JwtProperty jwtProperty;

    @Resource
    private TokenProperty tokenProperty;

    @Resource
    private Redis redis;

    /**
     * 判断用户是否有权限
     *
     * @param authentication 用户权限标识
     * @param url 请求地址
     * @param method 请求方法
     * @return 是否有权限
     */
    @Override
    public boolean hasPermission(String authentication, String url, String method) {
        Result<?> result = authorizationClient.hasPermission(authentication, url, method);
        return ResultHelper.hasSuccess(result);
    }

    /**
     * 判断请求是否在白名单中
     *
     * @param url 请求地址
     * @return 是否在白名单中
     */
    @Override
    public boolean isWhitelistedRequest(String url) {
        List<String> whiteList = gatewayProperty.getWhiteList();
        if (whiteList != null && !whiteList.isEmpty()) {
            if (whiteList.stream().anyMatch(url::startsWith)) {
                return true;
            }
        }

        List<String> noAuthUrls = gatewayProperty.getNoAuthUrls();
        if (noAuthUrls.contains(url)) {
            return true;
        }

        String apiUriSuffix = gatewayProperty.getApiUriSuffix();
        return url.endsWith(apiUriSuffix);
    }

    /**
     * 判断token是否认证通过
     *
     * @param authentication 用户权限标识
     * @param url 请求地址
     * @param cookies 请求cookies
     * @return token是否认证通过
     */
    @Override
    public boolean isTokenAuthenticated(String authentication, String url, MultiValueMap<String, HttpCookie> cookies) {
        if (StringUtils.isEmpty(authentication)) {
            boolean isSpecialUrl = false;
            for (String specialUrl : gatewayProperty.getSpecialAuthUrls()) {
                if (specialUrl.contains("*")) {
                    if (url.startsWith(specialUrl.replace("*", ""))) {
                        isSpecialUrl = true;
                        break;
                    }
                } else {
                    if (url.equals(specialUrl)) {
                        isSpecialUrl = true;
                        break;
                    }
                }
            }
            if (isSpecialUrl) {
                HttpCookie first = cookies.getFirst(BasicConstant.TOKEN_NAME);
                authentication = first != null ? first.getValue() : "";
            }
        }

        if (StringUtils.isEmpty(authentication)) {
            throw new BasicException(ResultCode.SystemResultCode.SYSTEM_NOT_LOGIN);
        }

        if (TokenConstant.TOKEN_MODE_JWT.equals(tokenProperty.getTokenMode())) {
            boolean isTokenValid;
            try {
                UserMeta ignore = new JwtCore<>(UserMeta.class).analyzeToken(authentication, jwtProperty.getPublicKey());
                isTokenValid = true;
            } catch (Exception ignore) {
                isTokenValid  = false;
            }

            if (isTokenValid) {
                Boolean isHas = redis.hasKey(RedisCacheConstant.REDIS_BLACK_TOKEN_PREFIX + authentication);
                if (isHas == null) {
                    return false;
                }
                return !isHas;
            } else {
                return false;
            }

        } else if (TokenConstant.TOKEN_MODE_JWT_UUID.equals(tokenProperty.getTokenMode())) {
            boolean isTokenValid;
            try {
                UserMeta ignore = new JwtCore<>(UserMeta.class).analyzeToken(authentication, jwtProperty.getPublicKey());
                isTokenValid = true;
            } catch (Exception ignore) {
                isTokenValid = false;
            }

            if (isTokenValid) {
                Boolean isHas = redis.hasKey(RedisCacheConstant.REDIS_BLACK_TOKEN_PREFIX + authentication);
                if (isHas == null) {
                    return false;
                }
                if (isHas) {
                    return false;
                }

                Object data = redis.get(authentication);
                if (data == null) {
                    throw new BasicException(ResultCode.SystemResultCode.SYSTEM_NOT_LOGIN);
                }
                if (data instanceof UserExtra) {
                    throw new NoLoginException((UserExtra) data);
                }
                return true;
            } else {
                return false;
            }

        } else if (TokenConstant.TOKEN_MODE_UUID.equals(tokenProperty.getTokenMode())) {
            Object data = redis.get(authentication);
            if (data == null) {
                throw new BasicException(ResultCode.SystemResultCode.SYSTEM_NOT_LOGIN);
            }
            if (data instanceof UserExtra) {
                throw new NoLoginException((UserExtra) data);
            }
            return true;

        }

        else return TokenConstant.TOKEN_MODE_NONE.equals(tokenProperty.getTokenMode());
    }
}
